#ifndef _CRF_MINABLE_H
#define _CRF_MINABLE_H

#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#include <boost/multi_array.hpp>

// local includes
#include "param.h"
#include "data.h"
#include "lbfgs.h"

class SparseMatrix;

// useful array types
typedef boost::multi_array<double, 2> Array2d;
typedef boost::multi_array<double, 3> Array3d;
typedef boost::multi_array<double, 4> Array4d;

// miniable class
class CRFMinable
{
    struct IterationData
    {
        IterationData() : function_evals(0), iteration_secs(0), total_secs(0), 
                          objective(0), last_objective(0) {}
        int function_evals;
        int iteration_secs;
        int total_secs;
        double objective;
        double last_objective;
    };
	typedef boost::shared_ptr<IterationData> IterationDataPtr;

public:
    CRFMinable(EventsPtr data, GPParameter *param);
    virtual ~CRFMinable(); 

    virtual double objective_and_gradient(
		boost::shared_array<double>, double&, boost::shared_array<double>);

    virtual void minimise();

public:
    boost::shared_array<double> m_weights;
    int m_iterations;

protected:
    virtual void set_iteration(int iteration, double objective, double acc);
    virtual void _set_emperical_kernel_count( void );
    virtual double m_calculate_objective(EventPtr, double &R, Array4d &marginals);
    virtual void m_calculate_gradient(EventPtr, Array4d &marginals, 
		boost::shared_array<double> G);
    virtual void m_forward_backward(EventPtr v, Array3d&, Array4d&,
            Array2d &forward, Array2d &backward, bool print=false);
    virtual void m_dot_products(EventPtr e, Array3d &nodes, Array4d &cliques);

protected:
    GPParameter* m_param;
    bool m_debug;
    IterationData m_it_data;
    boost::shared_array<double> m_emperical_count;
    EventsPtr m_data;
    int m_rank, m_size;
};
typedef boost::shared_ptr<CRFMinable> CRFMinablePtr;

#endif // _CRF_MINABLE_H
